#pragma once

#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include <functional>
#include <memory>
#include "logger.hpp"

// 服务注册客户端类
class Registry
{
public:
    using ptr = std::shared_ptr<Registry>;
    Registry(const std::string &host,int ttl = 3)//ttl == expireTime
        : _client(std::make_shared<etcd::Client>(host)), // 创建etcd客户端
          _keep_alive(_client->leasekeepalive(ttl).get()), // 设置租约保活 3 秒
          _lease_id(_keep_alive->Lease())                // 设置租约id
    {}
    ~Registry() { _keep_alive->Cancel(); } // 析构时取消租约保活

    // 注册服务方法   key--服务名  val--服务地址 
    bool registry(const std::string &key, const std::string &val) //{key :val}
    {
        // 使用租约ID将服务信息（key为服务名，val为地址）存入etcd
        auto resp = _client->put(key, val, _lease_id).get(); // 绑定租约
        if (resp.is_ok() == false)
        {
            LOG_ERROR("新增数据失败: {}", resp.error_message());
            return false;
        }
        return true;
    }

private:
    std::shared_ptr<etcd::Client> _client;        // etcd客户端对象
    std::shared_ptr<etcd::KeepAlive> _keep_alive; // 租约保活对象  --定期(默认1秒1次)向etcd服务器发送保活
    uint64_t _lease_id;                           // 租约id
};

// ectd服务发现客户端类
class Discovery
{
    using NotifyCallback = std::function<void(std::string, std::string)>;

public:
    using ptr = std::shared_ptr<Discovery>;
    Discovery(const std::string &host,
              const std::string &basedir,
              const NotifyCallback &put_cb,
              const NotifyCallback &del_cb)
        : _client(std::make_shared<etcd::Client>(host)),
          _put_back(put_cb),
          _del_back(del_cb)
    {
        // 服务发现
        auto resp = _client->ls(basedir).get(); // 获取basedir 目录下所有服务
        if (resp.is_ok() == false)
        {
            LOG_ERROR("获取键值对失败: {}\n", resp.error_message());
            return;
        }
        // 遍历所有服务--为什么下方的callback中的逻辑和下面for循环的逻辑类似？？
        // 因为服务发现对象实例化的时候，有可能有些子服务没有上线，或者有提供相同子服务的主机节点上线了，
        // 所以此时就需要有一个服务变化的监听器，即callback事件回调函数来处理服务的上线or下线
        int sz = resp.keys().size();
        for (int i = 0; i < sz; ++i)
        {
            LOG_INFO("{} 可以提供: {}\n", resp.value(i).as_string(), resp.key(i));
            _put_back(resp.key(i), resp.value(i).as_string());
        }

        //创建监听器，监控basedir下的服务变更
        _watcher = std::make_shared<etcd::Watcher>(*_client.get(),
                                                    basedir,
                                                    std::bind(&Discovery::callback,this,std::placeholders::_1),
                                                    true);
    }
    ~Discovery()
    {
        _watcher->Cancel(); // 析构时取消监听
    }

private:
    // 事件回调函数：处理服务上线/下线事件
    void callback(const etcd::Response &resp)
    {
        if (resp.is_ok() == false)
        {
            LOG_ERROR("收到一个错误事件通知: {}\n", resp.error_message());
            return;
        }
        // 遍历所有事件
        for (auto const &ev : resp.events())
        {
            // 处理上线事件
            if (ev.event_type() == etcd::Event::EventType::PUT)
            {
                if (_put_back)
                {
                     _put_back(ev.kv().key(), ev.kv().as_string());
                }
                LOG_DEBUG("新增服务: {}-{}\n", ev.kv().key(), ev.kv().as_string());
            }
            // 处理下线事件
            else if (ev.event_type() == etcd::Event::EventType::DELETE_)
            {
                if (_del_back)
                {
                     _del_back(ev.prev_kv().key(), ev.prev_kv().as_string());
                }
                LOG_DEBUG("下线服务: {}-{}\n", ev.prev_kv().key(), ev.prev_kv().as_string());
            }
        }
    }

private:
    NotifyCallback _put_back;                // 服务上线回调函数  void(string, string)
    NotifyCallback _del_back;                // 服务下线回调函数
    std::shared_ptr<etcd::Client> _client;   // etcd客户端
    std::shared_ptr<etcd::Watcher> _watcher; // 事件监听器
};